package com.game.commons.configuration;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Properties;

import org.apache.log4j.Logger;

/**
 * 将Properties复制给配置类
 * @author 杜祥
 * 需要包:
 * 	log4j-1.2.16.jar
 */
public class ConfigurableProcessor 
{
	private static Logger log = Logger.getLogger(ConfigurableProcessor.class);
	
	/**
	 * 加载配置文件
	 * @param object
	 * @param properties
	 */
	@SuppressWarnings("rawtypes")
	public static void process(Object object,Properties... properties)
	{
		Class clazz;
		
		if(object instanceof Class)
		{
			clazz = (Class) object;
			object = null;
		}
		else
		{
			clazz = object.getClass();
		}
		process(clazz, object, properties);
	}
	
	/**
	 * 
	 * @param clazz
	 * @param obj
	 * @param props
	 */
	@SuppressWarnings("rawtypes")
	private static void process(Class clazz, Object obj, Properties[] props)
	{
		processFields(clazz, obj, props);
		if (obj == null)
		{
			/**确定此对象所表示的类或接口实现的接口。**/
			for (Class itf : clazz.getInterfaces())
			{
				process(itf, obj, props);
			}
		}
		/**返回表示此 Class 所表示的实体（类、接口、基本类型或 void）的超类的 Class。**/
		Class superClass = clazz.getSuperclass();
		if (superClass != null && superClass != Object.class)
		{
			process(superClass, obj, props);
		}
	}
	
	
	/**
	 * 判断是否满足加载条件
	 * @param clazz
	 * @param object
	 * @param properties
	 */
	@SuppressWarnings("rawtypes")
	private static void processFields(Class clazz,Object object,Properties... properties)
	{
		/**得到该对象所有的元素**/
		for(Field f : clazz.getDeclaredFields())
		{
			if(Modifier.isStatic(f.getModifiers()) && object != null)
			{
				continue;
			}
			if(!Modifier.isStatic(f.getModifiers()) && object == null)
			{
				continue;
			}
			/**判断该元素是否有该类型的注释**/
			if(f.isAnnotationPresent(Property.class))
			{
				if(Modifier.isFinal(f.getModifiers()))
				{
					RuntimeException re = new RuntimeException("final修饰的属性无法复制,属性名:" + f.getName() + " 类名: " + clazz.getName());
					log.warn(re);
					throw re;
				}
				else
				{
					processField(f, object, properties);
				}
			}
		}
	}
	
	/**
	 * 查找是否有与field匹配的值
	 * @param f
	 * @param obj
	 * @param properties
	 */
	private static void processField(Field f,Object obj,Properties... properties)
	{
		boolean oldAccessible = f.isAccessible();
		f.setAccessible(true);
		/**得到元素制定类型的注释**/
		Property property = f.getAnnotation(Property.class);
		try 
		{
			/**判断对象值是否与默认值相等||是否有与该对象对应的值**/
			if(!Property.DEFAULT_VALUE.equals(property.defaultValue()) || isKeyPresent(property.key(), properties))
			{
				f.set(obj, getFieldValue(f, properties));//设置值
			}
			else if(log.isDebugEnabled())
			{
				log.debug("Field " + f.getName() + " class " + f.getDeclaringClass().getName() + " 没有找到该属性的值");
			}
		}
		catch (Exception e)
		{
			RuntimeException re = new RuntimeException("不能转换域" + f.getName() + " class " + f.getDeclaringClass(), e);
			log.error(re);
			throw re;
		}
		f.setAccessible(oldAccessible);
	}
	
	/**
	 * 得到field的值
	 * @param f
	 * @param properties
	 * @return
	 */
	@SuppressWarnings("rawtypes")
	private static Object getFieldValue(Field f,Properties[] properties)
	{
		Property property = f.getAnnotation(Property.class);//得到指定类型的注释
		String defaulValue = property.defaultValue();//获得注释的默认值
		String key = property.key();//得到key
		String value = null;
		if(key.isEmpty())//key的length==0
		{
			log.warn("Property " + f.getName() + " class " + f.getDeclaringClass().getName() + " leng为0");
		}
		else
		{
			value = findPropertyBykey(key, properties);//得到元素的值
		}
		if(value == null)
		{
			value = defaulValue;
			log.warn("使用默认值，元素:" + f.getName() + " class " + f.getDeclaringClass().getName());
		}
		
		PropertyTransformer pt = PropertyTransformerFactory.newTransformer(f.getType(), property.propertyTransformer());
		
		return pt.transform(value, f);
	}
	
	
	/**
	 * 判断key是否能在配置文件中找到
	 * @param key
	 * @param properties
	 * @return
	 */
	private static boolean isKeyPresent(String key,Properties... properties)
	{
		return findPropertyBykey(key, properties) != null;
	}
	
	/**
	 * 查找制定键的值
	 * @param key
	 * @param properties
	 * @return
	 */
	private static String findPropertyBykey(String key,Properties[] properties)
	{
		for(Properties p : properties)
		{
			if(p.containsKey(key))
			{
				return p.getProperty(key);
			}
		}
		return null;
	}
}
